package top.zhangjianyong.blog.validation;

import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import java.lang.annotation.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义参数校验注解
 *
 * @author zhangjianyong
 * @since 2024/8/5 下午6:59
 */
@Documented
@Constraint(
    validatedBy = {
      ListValue.ListValueConstraintValidator.class,
      ListValue.ListStringValueConstraintValidator.class
    })
// 指定校验器，这里不指定时，就需要在初始化时指定
@Target({
  ElementType.METHOD,
  ElementType.FIELD,
  ElementType.ANNOTATION_TYPE,
  ElementType.CONSTRUCTOR,
  ElementType.PARAMETER,
  ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {

  String message() default "{jakarta.validation.constraints.NotNull.message}";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

  int[] values() default {}; // 数值数组，提交的值只能是数组里面

  String[] strValues() default {}; // 字符串数组，提交的值只能是数组里面

  class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private final Set<Integer> set = new HashSet<>();

    /** 初始化方法 */
    @Override
    public void initialize(ListValue constraintAnnotation) {
      int[] values = constraintAnnotation.values();
      for (int val : values) set.add(val);
    }

    /** 判断是否校验成功 */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
      return null == value || set.contains(value);
    }
  }

  class ListStringValueConstraintValidator implements ConstraintValidator<ListValue, String> {

    private final Set<String> set = new HashSet<>();

    /** 初始化方法 */
    @Override
    public void initialize(ListValue constraintAnnotation) {
      String[] values = constraintAnnotation.strValues();
      Collections.addAll(set, values);
    }

    /** 判断是否校验成功 */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
      return null == value || set.contains(value);
    }
  }
}
